﻿#include "AVDumer.h"
#include "AAC_Decoder.h"
#include "H264_Decoder.h"
#include <QDebug>

AVDumer::AVDumer(AVContext* ac)
    :avContext_(ac)
{
    aacDecoder_ = new AAC_Decoder(avContext_,this);
    h264Decoder_ = new H264_Decoder(avContext_,this);
}

int AVDumer::Open(const std::string &path)
{
    AVDictionary* dict = nullptr;
    if (avformat_open_input(&pFormateCtx_, path.c_str(), nullptr, &dict) != 0) {
        qDebug() << "avformat_open_input failed.";
        return -1;
    }
    if(dict)
    {
        av_dict_free(&dict);
    }
    //是否含有音视频
    if (avformat_find_stream_info(pFormateCtx_, nullptr) < 0) {
        qDebug() << "avformat_find_stream_info failed.";
        return -2;
    }

    //遍历音视频流
    for (unsigned i = 0; i < pFormateCtx_->nb_streams; ++i)
    {
        if (AVMEDIA_TYPE_VIDEO == pFormateCtx_->streams[i]->codecpar->codec_type
            && videoIndex < 0) {
            videoIndex = i;
            avContext_->videoDuration = pFormateCtx_->streams[i]->duration * av_q2d(pFormateCtx_->streams[i]->time_base);
            avContext_->video_src_timebase = pFormateCtx_->streams[i]->time_base;
            avContext_->video_dst_timebase.num = pFormateCtx_->streams[i]->avg_frame_rate.den;
            avContext_->video_dst_timebase.den = pFormateCtx_->streams[i]->avg_frame_rate.num;
        }
        if (AVMEDIA_TYPE_AUDIO == pFormateCtx_->streams[i]->codecpar->codec_type
            && audioIndex < 0) {
            audioIndex = i;
            avContext_->audio_src_timebase = pFormateCtx_->streams[i]->time_base;
            avContext_->audioDuration = pFormateCtx_->streams[i]->duration * av_q2d(pFormateCtx_->streams[i]->time_base);
            continue;
        }
    }
    if (videoIndex == -1 && audioIndex == -1)
    {
        return -1;
    }
    if(videoIndex != -1)
    {
        if(h264Decoder_->Open(pFormateCtx_->streams[videoIndex]->codecpar) != 0)
        {
            return -1;
        }
        avContext_->avstream_ |= VIDEO;
    }
        if(audioIndex != -1)
        {
            if(aacDecoder_->Open(pFormateCtx_->streams[audioIndex]->codecpar) != 0)
            {
                return -1;
            }
            avContext_->avstream_ |= AUDIO;
        }
    //启动子线程
    start();
    return 0;
}

void AVDumer::run()
{
    AVPacketPtr packet = nullptr;
    while (!quit_ && aacDecoder_ && h264Decoder_ && pFormateCtx_) {
        if (aacDecoder_->isFull() ||
            h264Decoder_->isFull()) {
            std::this_thread::sleep_for(std::chrono::milliseconds(1));
            continue;
        }
        packet = AVPacketPtr(av_packet_alloc(), [](AVPacket* p) {av_packet_free(&p);});
        if (av_read_frame(pFormateCtx_, packet.get()) < 0) {
            aacDecoder_->put_packet(nullptr);
            h264Decoder_->put_packet(nullptr);
            continue;
        }
        if (packet->stream_index == audioIndex) {
            aacDecoder_->put_packet(packet);
        }
        else if (packet->stream_index == videoIndex) {
            h264Decoder_->put_packet(packet);
        }
        else {
            av_packet_unref(packet.get());
        }
    }
}

double AVDumer::audioDuration()
{
    return avContext_->audioDuration;
}

double AVDumer::videoDuration()
{
    return avContext_->videoDuration;
}

void AVDumer::Clear()
{
    avformat_flush(pFormateCtx_);
    aacDecoder_->Clear();
    h264Decoder_->Clear();
    avContext_->video_queue_.clear();
    avContext_->audio_queue_.clear();
}

bool AVDumer::setSpeed(const uint32_t &speed)
{
    return true;
}

int64_t AVDumer::setSeek(const double &pos)
{
    std::lock_guard<std::mutex> lock(mutex_); // 获取互斥锁
    Clear();
    int index = audioIndex == -1 ? videoIndex : audioIndex;
    double duration = audioIndex == -1 ? avContext_->videoDuration : avContext_->audioDuration;
    AVRational rational = audioIndex == -1 ? avContext_->video_dst_timebase : avContext_->audio_dst_timebase;
    int64_t pts = (pos * duration) / av_q2d(rational);
    int ret = av_seek_frame(pFormateCtx_, index, pts , AVSEEK_FLAG_BACKWARD);
    if (ret < 0) {
        return -1;
    }
    return pts;
}

void AVDumer::Close()
{
    quit_ = true;
    if(this->isRunning())
    {
        this->quit();
        this->wait();
    }
    if(pFormateCtx_)
    {
        avformat_close_input(&pFormateCtx_);
    }
    if(aacDecoder_)
    {
        delete aacDecoder_;
        aacDecoder_ = nullptr;
    }
    if(h264Decoder_)
    {
        delete h264Decoder_;
        h264Decoder_ = nullptr;
    }
}

AVDumer::~AVDumer()
{
    Close();
}
